package ca.venasse.pinventory.inventory;

import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.newHashMap;

import java.util.List;
import java.util.Map;

import org.axonframework.domain.AggregateIdentifier;
import org.axonframework.eventhandling.annotation.EventHandler;
import org.axonframework.eventsourcing.annotation.AbstractAnnotatedAggregateRoot;

import ca.venasse.pinventory.common.Money;
import ca.venasse.pinventory.common.Quantity;
import ca.venasse.pinventory.inventory.common.CategoryIdentifier;
import ca.venasse.pinventory.inventory.common.InventoryName;
import ca.venasse.pinventory.inventory.common.LocationIdentifier;
import ca.venasse.pinventory.inventory.common.LocationName;
import ca.venasse.pinventory.inventory.common.PartIdentifier;
import ca.venasse.pinventory.inventory.common.PartName;
import ca.venasse.pinventory.inventory.common.PartUrl;
import ca.venasse.pinventory.inventory.common.PurchaseDate;
import ca.venasse.pinventory.inventory.common.SupplierName;
import ca.venasse.pinventory.inventory.event.InventoryCreated;
import ca.venasse.pinventory.inventory.event.LocationCreated;
import ca.venasse.pinventory.inventory.event.ChildLocationCreated;
import ca.venasse.pinventory.inventory.event.LossRecorded;
import ca.venasse.pinventory.inventory.event.ParentOfLocationChanged;
import ca.venasse.pinventory.inventory.event.ParentOfLocationRemoved;
import ca.venasse.pinventory.inventory.event.PartAdded;
import ca.venasse.pinventory.inventory.event.PartPurchased;
import ca.venasse.pinventory.inventory.event.RecordedLossReversed;

public class Inventory extends AbstractAnnotatedAggregateRoot {

	private static final long serialVersionUID = 1L;

	private Map<LocationIdentifier, Location> locations;
	private List<Part> parts;

	public Inventory(AggregateIdentifier aggregateIdentifier) {
		super(aggregateIdentifier);
	}

	public Inventory(AggregateIdentifier identifier, InventoryName name) {
		super(identifier);
		apply(new InventoryCreated(name));
	}

	public void addPart(CategoryIdentifier categoryIdentifier, PartIdentifier partIdentifier,
			PartName name) {
		apply(new PartAdded(categoryIdentifier, partIdentifier, name));
	}

	public void changeParentOfLocation(LocationIdentifier parentLocationIdentifier,
			LocationIdentifier locationIdentifier) {
		apply(new ParentOfLocationChanged(parentLocationIdentifier, locationIdentifier));
	}

	public void createLocation(LocationIdentifier locationIdentifier, LocationName name) {
		apply(new LocationCreated(locationIdentifier, name));
	}

	public void createChildLocation(LocationIdentifier parentLocationIdentifier,
			LocationIdentifier locationIdentifier, LocationName name) {
		apply(new ChildLocationCreated(parentLocationIdentifier, locationIdentifier, name));
	}

	public void purchasePart(PartIdentifier partIdentifier, SupplierName supplierName,
			PartUrl partUrl, PurchaseDate purchaseDate, Quantity quantity, Money unitCost) {
		apply(new PartPurchased(
			partIdentifier,
			supplierName,
			partUrl,
			purchaseDate,
			quantity,
			unitCost));
	}

	public void recordLoss(PartIdentifier partIdentifier, Quantity quantity) {
		apply(new LossRecorded(partIdentifier, quantity));
	}

	public void removeParentOfLocation(LocationIdentifier locationIdentifier) {
		apply(new ParentOfLocationRemoved(locationIdentifier));
	}

	public void reverseRecordedLoss(PartIdentifier partIdentifier, Quantity quantity) {
		apply(new RecordedLossReversed(partIdentifier, quantity));
	}

	@EventHandler
	protected void z_handle(InventoryCreated event) {
		this.locations = newHashMap();
		this.parts = newArrayList();
	}

	@EventHandler
	protected void z_handle(LocationCreated event) {
		Location location = new Location(event.getLocationIdentifier(), event.getName());
		this.locations.put(event.getLocationIdentifier(), location);
	}

	@EventHandler
	protected void z_handle(ChildLocationCreated event) {
		Location location = new Location(event.getLocationIdentifier(), event.getName());
		this.locations.put(event.getLocationIdentifier(), location);
	}

	@EventHandler
	protected void z_handle(PartAdded event) {
		Part part = new Part(event.getPartIdentifier());
		this.parts.add(part);
	}

}
